/* 
 * Android Operation Queue
 * http://code.google.com/p/operation-queue/
 * 
 * Copyright (C) 2012 Dong-u Seo
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *     http://www.apache.org/licenses/LICENSE-2.0
 *     
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.minz.opqueue;

import java.util.Collection;
import java.util.LinkedList;

import android.content.res.Resources.Theme;
import android.os.AsyncTask;
import android.util.Log;

/**
 * 
 * @author dong_u
 *
 */
public class OperationQueue extends Thread {

	public final static int OPERATION_FINISHED_STATE = 0;

	private LinkedList<Operation> queue = new LinkedList<Operation>();
	private int MAX_SIMULTANEOUSLY_EXECUTED_TASK = 5;

	protected OperationMessageHandler handler;

	private int numOfAddedOperations = 0;

	@Override
	public void run() {
		super.run();

		while(!Thread.interrupted()){
			if(queue.isEmpty()){
				synchronized ( queue ) {
					try {
						queue.wait();
					} catch ( InterruptedException e ) {
						e.printStackTrace();
						return;
					}
				}
			}
			
			// end this thread when interrupt method is called  
			try{
				for(int i = 0 ; i < MAX_SIMULTANEOUSLY_EXECUTED_TASK ; i++ ){
					Operation task = queue.get(i);
					AsyncTask.Status s = task.getStatus();
					if(s.equals(AsyncTask.Status.PENDING)){
						task.execute(task.params);
					}
				}
			}catch (Exception e) {
				e.getStackTrace();
			}
			synchronized ( queue ) {
				try {
					queue.wait(500);
				} catch ( InterruptedException e ) {
					e.printStackTrace();
					return;
				}
			}
		}
	}

	public void setHandler( OperationMessageHandler handler ){
		this.handler = handler;
	}

	/**
	 * Set the number of {@code Operation}s execute simultaneously. 
	 * @param max
	 */
	public void setNumberOfMaxSimTasks(int max){
		if(!this.getState().equals( State.NEW )){
			throw new IllegalStateException( "After this thread started you cannot use this method." );
		}
		MAX_SIMULTANEOUSLY_EXECUTED_TASK = max;
	}

	/**
	 * Add Operation. 
	 * @param object
	 * @return
	 */
	public boolean add(Operation object) throws IllegalStateException{
		if( getState().equals( State.TERMINATED ) ){
			throw new IllegalStateException( "Terminated Thread" );
		}
		if ( !object.getStatus().equals( AsyncTask.Status.PENDING )){
			throw new IllegalStateException( "You cannot add executed Operation." );
		}
		object.setQueue( this );
		numOfAddedOperations++;
		synchronized ( queue ) {
			queue.notifyAll();
		}
		return queue.add(object);
	}

	/**
	 * Add Operations.
	 * @param collection
	 * @return
	 */
	public boolean addAll( Collection<? extends Operation> collection) {
		for(Operation obj : collection){
			if( getState().equals( State.TERMINATED ) ){
				throw new IllegalStateException( "Terminated Thread" );
			}
			if ( !obj.getStatus().equals( AsyncTask.Status.PENDING )){
				throw new IllegalStateException( "You cannot add executed Operation." );
			}
			obj.setQueue( this );
		}
		numOfAddedOperations += collection.size();
		return queue.addAll(collection);
	}

	/**
	 * 
	 * @param object
	 */
	protected void removeOperationOnPost(Object object){
		queue.remove(object);
		synchronized ( queue ) {
			queue.notify();
		}
		if(handler != null){
			handler.obtainMessage( OperationMessageHandler.QUEUE_ON_A_OPERATION_FINISHED 
					, queue.size() , numOfAddedOperations ).sendToTarget();
		}
		if(queue.isEmpty() && handler != null){
			handler.sendEmptyMessage( OperationMessageHandler.QUEUE_FINISHED );
		}
	}

	/**
	 * 
	 * @param object
	 */
	protected void removeOperationOnCancel(Object object){
		queue.remove(object);
		synchronized ( queue ) {
			queue.notify();
		}
		if(handler != null){
			handler.obtainMessage( OperationMessageHandler.QUEUE_ON_A_OPERATION_CANCELED 
					, queue.size() , numOfAddedOperations ).sendToTarget();
		}
		if(queue.isEmpty() && handler != null){
			handler.sendEmptyMessage( OperationMessageHandler.QUEUE_FINISHED );
		}
	}

	/**
	 * Get size of queue. 
	 * @return
	 */
	public int size() {
		return queue.size();
	}
	
	/**
	 * In this method all the Operations which is executed is canceled and
	 * the queue is cleared, and this thread is finished.  
	 */
	@Override
	public void interrupt() {
		for(Operation task : queue){
			AsyncTask.Status s = task.getStatus();
			if(		!s.equals(AsyncTask.Status.PENDING ) && 
					!s.equals(AsyncTask.Status.FINISHED)){
				task.cancel(true);
			}
		}
		queue.clear();
		super.interrupt();
	}
}